package com.zzx.controller;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

public class Main extends GregorianCalendar{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	/** 使用当前时间构造一个实例。 */
    public Main() {
        super();
    }
    /**
     * 使用指定日期构造一个实例。
     * @param isChinese 是否为农历日期
     * @param y
     * @param m
     * @param d
     */
 
    public Main(boolean isChinese, int y, int m, int d) {
    	if (isChinese) {
            set(CHINESE_YEAR, y);
            set(CHINESE_MONTH, m);
            set(CHINESE_DATE, d);
        } else {
            set(y, m, d);
        }
    }
	public static void main(String[] args) throws ParseException {
		int y = 2019;
		int m = 4;
		int d = 29;		
		//阳历日期转为阴历
		int arr[] = dateChang(y,m,d);
//		System.err.println("**公历** "+y+"-"+m+"-"+d);
		System.err.println("**农历** "+arr[0]+" 年 "+arr[1]+" 月 "+arr[2]+" 日");
		String yStr = getGanZhi(arr[0]);
		String mStr = ymSong(yStr,arr[1]);
		String anStr = getYearZoor(arr[0]);
		String dStr = getDayGanZhi(y,m,d);
		System.err.println("干支  :"+yStr+"("+anStr+")"+" 年 "+ mStr+" 月 "+ dStr + " 日");		
		Main ma = new Main(true,2019,3,15);
		String st = ma.getSimpleGregorianDateString();
		System.err.println("***公历**"+st);
	}	
	private static final char[] daysInGregorianMonth = { 31, 28, 31, 30, 31,			 
		    30, 31, 31, 30, 31, 30, 31 };
	/**
	 * 获取月份的天数。
	 * @return
	 */
	public static int getPubMondn(int y,int m) {
		 int d = daysInGregorianMonth[m];		 
        if (m == Calendar.FEBRUARY && LeapYear(y)) { 
            d++; // 公历闰年二月多一天 
        }
		return d;
	}
	/**
	 * 获取 日的 干支。
	 * @param y
	 * @param m
	 * @param d
	 * @return
	 * @throws ParseException
	 */
	public static String getDayGanZhi(int y,int m ,int d) throws ParseException {
		int offset = (int) getAllDays(y,m,d); 
		int n = offset + 40;
        int num = n%60 ;
		return gzArr[num];
	}
	public static String getYearZoor(int y) {
		int aa = (y-4)%12;
        String str = Animals[aa];
		return str;
	}
	/**
	 * 获取月份的干支
	 * @param month
	 * @return
	 */
	public static String getMonthGanZhi(String oStr ,int month) {
		String s = ganTozhi(oStr,month);
		return s;
	}
	public static String ganTozhi(String oStr , int num) {
		int n =0;
		for(int i=0;i<10;i++) {
			if(oStr.equals(Gan[i])) {
				n = i;
				break;
			}
		}
		String [] strArr = new String[12];
		for(int j =0;j<12;j++) {
			StringBuilder str = new StringBuilder();
			str.append(Gan[n]).append(mZhi[j]);
			n++;
			n = (n==10)?0:n;
			strArr[j] = str.toString();
		}
		return strArr[num-1];
	}
	public static String ymSong(String str,int m) {
		String gan = str.substring(0, 1);		
		String rtnStr = "";
		if("甲".equals(gan) || "己".equals(gan)) {
			rtnStr = getMonthGanZhi("丙",m);
		}else if("乙".equals(gan) || "庚".equals(gan)) {
			rtnStr = getMonthGanZhi("戊",m);
		}else if("丙".equals(gan) || "辛".equals(gan)) {
			rtnStr = getMonthGanZhi("庚",m);
		}else if("丁".equals(gan) || "壬".equals(gan)) {
			rtnStr = getMonthGanZhi("壬",m);
		}else if("戊".equals(gan) || "癸".equals(gan)) {
			rtnStr = getMonthGanZhi("甲",m);
		}
		System.err.println("*3333**"+rtnStr);
		return rtnStr;
	}
	/**
	 * 获取年份胡干支。
	 * @param year
	 * @return
	 */
	public static String getGanZhi(int year) {
		String rtnStr = gzArr[(year-4)%60];
		return rtnStr;
	}
	/**
	 * 公历转为农历
	 * @param y
	 * @param m
	 * @param d
	 * @return
	 * @throws ParseException
	 */
	public static int[] dateChang(int y,int m ,int d) throws ParseException {
		long alD = getAllDays(y,m,d);		
		int[] arr = forYear(y,alD);
		return arr;
	}
	public static int getYear(int year) {
		int num = LunarDateArray[year-1900];		
		return num;
	}
   
	/**
	 * 获取当前的月份
	 */
	public static int[] forYear(int y ,long ad) {
		int i = 0,temp = 0;
		for (i = 1900; i < 2050 && ad > 0; i++) {
            temp = getYearDays(i);
            ad -= temp;
        }
		if (ad < 0) {
			ad += temp;
            i--;
        }
		return getDay(y,ad);
	}
	/**
	 * 获取传递的日期是当前年的几号，农历。
	 * @return
	 */
	public static int[] getDay(int y ,long d) {
		int i = 0,temp = 0;
		boolean runFlag = true;
		int m = leapMonth(y);//获取当年是否有闰月。4 17
		for(i = 1;i<13 && d >0;i++) {	//43312	
			if(m > 0 && i == (m + 1) && runFlag) {				
				runFlag = false;
				--i;
				temp = leapDays(y);
			}else {
				temp = getMonthDay(y,i);
			}			
			d -= temp;	
			if(runFlag && i== (m+1)) {
				runFlag = true;
			}
		}
		if(d<0) {
			d += temp;
			i--;
		}
		//0x07954
		int[] rtnDate = new int[7];
		rtnDate[0]=y;
		rtnDate[1]=i;
		rtnDate[2]=(int)d+1;
		return rtnDate;
	}
	/**
	 * 获取指定月的天数
	 * @param y
	 * @param m
	 * @return
	 */
	public static int getMonthDay(int y,int m) {
		if ((LunarDateArray[y - 1900] & (0x10000 >> m)) == 0) {
			return 29;
		}else {
			return 30;
		}
	}
	/**
	 * 获取指定年的总天数。
	 * @param year
	 */
	public static int getYearDays(int year) {
		int allDays = 348;
		int i;
		for(i= 0x8000;i>0x8;i>>=1) {
			if ((LunarDateArray[year - 1900] & i) != 0)
				allDays += 1;
		}
		return allDays+leapDays(year);
	}
	/**
     * 传回农历 y年闰月的天数
     * 
     * @param y
     * @return
     */
    final private static int leapDays(int y) {
        if (leapMonth(y) != 0) {//最后一位不等于0 则为改年有闰月。
        	//第17位：表示闰月天数，0表示29天   1表示30天
            if ((LunarDateArray[y - 1900] & 0x10000) != 0)
                return 30;
            else
                return 29;
        } else
            return 0;
    }
    /**
     	* 传回农历 y年闰哪个月 1-12 , 没闰传回 0
     	* 末四位与 1111 进行与操作，返回末四位的值。
     	* @param y
     	* @return
     */
    final private static int leapMonth(int y) {
        return (int) (LunarDateArray[y - 1900] & 0xf);
    }

	/**
	   * 获取从传输日期到 1900年 农历春节的天数。
	 * @param y
	 * @param m
	 * @param d
	 * @return
	 * @throws ParseException 
	 */
	public static long getAllDays(int y , int m ,int d) throws ParseException {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");    
		Date oldDate = sdf.parse("1900-1-31");  		
		Calendar today = Calendar.getInstance();   
        today.setTime(sdf.parse(y+"-"+m+"-"+d)); 
		long n = (today.getTime().getTime() - oldDate.getTime())/(60*60*24)/1000;
		return n;
	}
	public static int getMonth(int year,int month,int day) {
		Date oldDate = new GregorianCalendar(0 + 1970, 2, 6).getTime();
        //设置当前时间
        Date objDate = new GregorianCalendar(year, month - 1, day).getTime();
        //将当前时间 减去 计算机初始时间 除以 每天的毫秒数 得到总差值天数
        long offset = (objDate.getTime() - oldDate.getTime()) / 86400000L;
        offset +=31;
        System.err.println("*月*"+offset);
		return 0;
	}
	/**
	 * 判断该年是否是闰年
	 */
	private static boolean LeapYear(int year) {
		if((year%4==0 && year %100 != 0) || year %400 ==0) {
			return true;
		}
		return false;
	}	

	
     /// 来源于网上的农历数据
     /// </summary>
     /// <remarks>
     /// 数据结构如下，共使用17位数据
     /// 第17位：表示闰月天数，0表示29天   1表示30天
     /// 第16位-第5位（共12位）表示12个月，其中第16位表示第一月，如果该月为30天则为1，29天为0
     /// 第4位-第1位（共4位）表示闰月是哪个月，如果当年没有闰月，则置0
	 // 2019为例：0x0A930
	 // 0000 1010 1001 0011 0000
	 // 30 29 30 29 30 29 29  30 29 29 30 30 
	 //2018为例：0x052B0
	 // 0000 0101 0010 1011 0000
	 //2017 0x15176 润 6 月
	 // 0001 0101 0001 0111 0110
	 //      29 30 29 30 29 29 29 30 29 30 30 30 29 30 30 29
     ///</remarks>
     private static int[] LunarDateArray = new int[]{
         0x04BD8,0x04AE0,0x0A570,0x054D5,0x0D260,0x0D950,0x16554,0x056A0,0x09AD0,0x055D2,//1900 - 1909
         0x04AE0,0x0A5B6,0x0A4D0,0x0D250,0x1D255,0x0B540,0x0D6A0,0x0ADA2,0x095B0,0x14977,//1910 - 1919
         0x04970,0x0A4B0,0x0B4B5,0x06A50,0x06D40,0x1AB54,0x02B60,0x09570,0x052F2,0x04970,//1920 - 1929
         0x06566,0x0D4A0,0x0EA50,0x06E95,0x05AD0,0x02B60,0x186E3,0x092E0,0x1C8D7,0x0C950,//1930 - 1939
         0x0D4A0,0x1D8A6,0x0B550,0x056A0,0x1A5B4,0x025D0,0x092D0,0x0D2B2,0x0A950,0x0B557,//1940 - 1949
         0x06CA0,0x0B550,0x15355,0x04DA0,0x0A5B0,0x14573,0x052B0,0x0A9A8,0x0E950,0x06AA0,//1950 - 1959
         0x0AEA6,0x0AB50,0x04B60,0x0AAE4,0x0A570,0x05260,0x0F263,0x0D950,0x05B57,0x056A0,//1960 - 1969
         0x096D0,0x04DD5,0x04AD0,0x0A4D0,0x0D4D4,0x0D250,0x0D558,0x0B540,0x0B6A0,0x195A6,//1970 - 1979
         0x095B0,0x049B0,0x0A974,0x0A4B0,0x0B27A,0x06A50,0x06D40,0x0AF46,0x0AB60,0x09570,//1980 - 1989
         0x04AF5,0x04970,0x064B0,0x074A3,0x0EA50,0x06B58,0x055C0,0x0AB60,0x096D5,0x092E0,//1990 - 1999
         0x0C960,0x0D954,0x0D4A0,0x0DA50,0x07552,0x056A0,0x0ABB7,0x025D0,0x092D0,0x0CAB5,//2000 - 2009
         0x0A950,0x0B4A0,0x0BAA4,0x0AD50,0x055D9,0x04BA0,0x0A5B0,0x15176,0x052B0,0x0A930,//2010 - 2019
         0x07954,0x06AA0,0x0AD50,0x05B52,0x04B60,0x0A6E6,0x0A4E0,0x0D260,0x0EA65,0x0D530,//2020 - 2029
         0x05AA0,0x076A3,0x096D0,0x04BD7,0x04AD0,0x0A4D0,0x1D0B6,0x0D250,0x0D520,0x0DD45,//2030 - 2039
         0x0B5A0,0x056D0,0x055B2,0x049B0,0x0A577,0x0A4B0,0x0AA50,0x1B255,0x06D20,0x0ADA0,//2040 - 2049
         0x14B63};
     private final static String[] gzArr = new String[] { 
	    		"甲子", "乙丑", "丙寅", "丁卯", "戊辰", "己巳", "庚午", "辛未", "壬申", "癸酉" ,
	    		"甲戌", "乙亥", "丙子", "丁丑", "戊寅", "己卯", "庚辰", "辛巳", "壬午", "癸未" ,
	    		"甲申", "乙酉", "丙戌", "丁亥", "戊子", "己丑", "庚寅", "辛卯", "壬辰", "癸巳" ,
	    		"甲午", "乙未", "丙申", "丁酉", "戊戌", "己亥", "庚子", "辛丑", "壬寅", "癸卯" ,
	    		"甲辰", "乙巳", "丙午", "丁未", "戊申", "己酉", "庚戌", "辛亥", "壬子", "癸丑" ,
	    		"甲寅", "乙卯", "丙辰", "丁巳", "戊午", "己未", "庚申", "辛酉", "壬戌", "癸亥" 
	            };
    private final static String[] Animals = new String[]{"鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪"};    
    private final static String[] Gan = new String[] { "甲", "乙", "丙", "丁", "戊",
            "己", "庚", "辛", "壬", "癸" };
    private final static String[] Zhi = new String[] { "子", "丑", "寅", "卯", "辰",
            "巳", "午", "未", "申", "酉", "戌", "亥" };
    private final static String[] mZhi = new String[] { "寅", "卯", "辰",
            "巳", "午", "未", "申", "酉", "戌", "亥","子", "丑"  };
    /** 农历年 */ 
    public static final int CHINESE_YEAR = 801; 
    /** 农历月 */ 
    public static final int CHINESE_MONTH = 802; 
    /** 农历日 */ 
    public static final int CHINESE_DATE = 803; 
    private int chineseYear;
    private int chineseMonth; // 1起始，负数表示闰月
    private int chineseDate;
    private boolean areChineseFieldsComputed; // 农历日期是否已经经过计算确认
    private boolean lastSetChinese; // 最后设置的是不是农历属性
    
 
    public void set(int field, int value) {
        computeIfNeed(field);
        if (isChineseField(field)) {
            // 农历属性
            switch (field) {
            case CHINESE_YEAR:
                chineseYear = value;
                break;
            case CHINESE_MONTH:
                chineseMonth = value;
                break;
            case CHINESE_DATE:
                chineseDate = value;
                break;
            default:
                throw new IllegalArgumentException("不支持的field设置：" + field);
            }
            lastSetChinese = true;
        } else {
            // 非农历属性
            super.set(field, value);
            lastSetChinese = false;
        }
        areFieldsSet = false;
        areChineseFieldsComputed = false;
    }
    public int get(int field) {
        computeIfNeed(field);
        if (!isChineseField(field)) {
            return super.get(field);
        }
        if (!areChineseFieldsComputed) {
            // 计算农历属性
            computeChineseFields();
            areChineseFieldsComputed = true;
        }        
        switch (field) {
        case CHINESE_YEAR:
            return chineseYear;
        case CHINESE_MONTH:
            return chineseMonth;
        case CHINESE_DATE:
            return chineseDate;
        default:
            throw new IllegalArgumentException("不支持的field获取：" + field);
        }
    }
    /**
     * 返回公历日期
     * @return
     */
    public String getSimpleGregorianDateString() {
        return new StringBuffer().append(get(YEAR))
        .append("-")
        .append(get(MONTH) + 1)
        .append("-")
        .append(get(DATE))
        .toString();
    }
 
    
 
    /**
     * 判断是不是农历属性
     * @param field
     * @return
     */ 
    private boolean isChineseField(int field) {
        switch (field) {
        case CHINESE_YEAR:
        case CHINESE_MONTH:
        case CHINESE_DATE:
            return true;
        default:
            return false;
        }
    }
 
    /**
     * 如果上一次设置的与这次将要设置或获取的属性不是同一类（农历/公历），<br>
     * 例如上一次设置的是农历而现在要设置或获取公历，<br>
     * 则需要先根据之前设置的农历日期计算出公历日期。
     * @param field
     */ 
    private void computeIfNeed(int field) {
        if (isChineseField(field)) {
            if (!lastSetChinese && !areChineseFieldsComputed) {
                super.complete();
                computeChineseFields();
                areFieldsSet = true;
                areChineseFieldsComputed = true;
            }
        } else {
            if (lastSetChinese && !areFieldsSet) {
                computeGregorianFields();
                super.complete();
                areFieldsSet = true;
                areChineseFieldsComputed = true;
            }
        }
    } 
    /**
     *
     * 使用农历日期计算出公历日期
     */ 
    private void computeGregorianFields() {
        int y = chineseYear;
        int m = chineseMonth;
        int d = chineseDate;
        areChineseFieldsComputed = true;
        areFieldsSet = true;
        lastSetChinese = false;
        // 调整日期范围
        if (y < 1900)
            y = 1899;
        else if (y > 2100)
            y = 2101;
        if (m < -12)
            m = -12;
        else if (m > 12)
            m = 12;
        if (d < 1)
            d = 1;
        else if (d > 30)
            d = 30;
        int dateint = y * 10000 + Math.abs(m) * 100 + d;
        if (dateint < 19001111) { // 太小
            set(1901, Calendar.JANUARY, 1);
            super.complete();
        } else if (dateint > 21001201) { // 太大
            set(2100, Calendar.DECEMBER, 31);
            super.complete();
        } else {
            if (Math.abs(m) > 12) {
                m = 12;
            }
            int days = AATest.daysInChineseMonth(y, m);
            if (days == 0) {
                m = -m;
                days = AATest.daysInChineseMonth(y, m);
            }
           
            if (d > days) {
                d = days;
            }
            set(y, Math.abs(m) - 1, d);
            computeChineseFields();
            int amount = 0;
            while (chineseYear != y || chineseMonth != m) {
                amount += daysInChineseMonth(chineseYear, chineseMonth);
                chineseMonth = nextChineseMonth(chineseYear, chineseMonth);
            }
            amount += d - chineseDate;
            super.add(Calendar.DATE, amount);
        }
        computeChineseFields();
    }
 
    /**
     *
     * 使用公历日期计算出农历日期
     */ 
    private void computeChineseFields() {
        int gregorianYear = internalGet(Calendar.YEAR);
        int gregorianMonth = internalGet(Calendar.MONTH) + 1;
        int gregorianDate = internalGet(Calendar.DATE);
        if (gregorianYear < 1901 || gregorianYear > 2100) {
            return;
        }
        int startYear = baseYear;
        int startMonth = baseMonth;
        int startDate = baseDate;
        chineseYear = baseChineseYear;
        chineseMonth = baseChineseMonth;
        chineseDate = baseChineseDate;
        // 第二个对应日，用以提高计算效率
        // 公历 2000 年 1 月 1 日，对应农历 4697 年 11 月 25 日
        if (gregorianYear >= 2000) {
            startYear = baseYear + 99;
            startMonth = 1;
            startDate = 1;
            chineseYear = baseChineseYear + 99;
            chineseMonth = 11;
            chineseDate = 25;
        }
        int daysDiff = 0;
        for (int i = startYear; i < gregorianYear; i++) {
            daysDiff += 365;
            if (isGregorianLeapYear(i)) {
                daysDiff += 1; // leap year
            }
        }
        for (int i = startMonth; i < gregorianMonth; i++) {
            daysDiff += daysInGregorianMonth(gregorianYear, i - 1);
        }
        daysDiff += gregorianDate - startDate;
        chineseDate += daysDiff;
        int lastDate = daysInChineseMonth(chineseYear, chineseMonth);
        int nextMonth = nextChineseMonth(chineseYear, chineseMonth);
        while (chineseDate > lastDate) {
            if (Math.abs(nextMonth) < Math.abs(chineseMonth)) {
                chineseYear++;
            } 
            chineseMonth = nextMonth; 
            chineseDate -= lastDate; 
            lastDate = daysInChineseMonth(chineseYear, chineseMonth); 
            nextMonth = nextChineseMonth(chineseYear, chineseMonth); 
        } 
    }
    /**
     * 是否为公历闰年
     * @param year
     * @return
     */ 
    public static boolean isGregorianLeapYear(int year) {
    	if((year%4==0&& year%100!=0) || year%400==0) {
    		return true;
    	}
        return false; 
    }
 
    /**
     * 计算公历年的当月天数，公历月从0起始！
     * @param y
     * @param m
     * @return
     */
    public static int daysInGregorianMonth(int y, int m) { 
        int d = daysInGregorianMonth[m]; 
        if (m == Calendar.FEBRUARY && isGregorianLeapYear(y)) { 
            d++; // 公历闰年二月多一天 
        } 
        return d; 
    }
 
    /**
     * 计算农历年的天数
     * @param y
     * @param m
     * @return
     */
     public static int daysInChineseMonth(int y, int m) {
        // 注意：闰月 m < 0   
        int d = 30; 
        if (1 <= m && m <= 12) {
       	 	d = getMonthDay(y,m);
        }else {
    	   if (leapMonth(y) != 0) {//最后一位不等于0 则为改年有闰月。
       		if ((LunarDateArray[y - 1900] & 0x10000) != 0)
       			d =30;
       		else
       			d = 29;
	       	}else {
	       		d =0;
	       	} 
       }
        return d;
    } 
    /**
     * 计算农历的下个月
     * @param y
     * @param m
     * @return
     */ 
    public static int nextChineseMonth(int y, int m) { 
        int n = Math.abs(m) + 1; 
        if (m > 0) { 
            int v = getRmonth(y);//如果是该年有闰月
            if (v == m) { 
                n = -m; 
            } 
        } 
        if (n == 13) { 
            n = 1; 
        } 
        return n; 
    }
    /**
     * 获取那个月是闰月
     * @param y
     * @return
     */
    public static int getRmonth(int y) {
    	return (LunarDateArray[y - 1900] & 0xf);
    }
    /* 日历第一天的日期 */ 
    private static final int baseYear = 1901; 
    private static final int baseMonth = 1; 
    private static final int baseDate = 1; 
    private static final int baseChineseYear = 1900; 
    private static final int baseChineseMonth = 11; 
    private static final int baseChineseDate = 11;     
}
